#include "lvgl_gui_lib.h"

#include "button.h"

#if CONFIG_LVGL_GUI_GUIDER

lv_ui guider_ui;

lv_group_t *btn_group;

#endif

void boot_btn_button_cb(struct _lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
    uint32_t key = 0;
    key = scanf_key();
    if (key != 0) {
        data->state = LV_INDEV_STATE_PR;
        data->key = key;
    } else {
        data->state = LV_INDEV_STATE_REL;
    }
}

void keypad_read(lv_indev_drv_t *drv, lv_indev_data_t *data) {
    static uint32_t last_key = 0;

    uint32_t act_key = get_button_val();

    if(act_key != 0) {
        data->state = LV_INDEV_STATE_PR;

        /*Translate the keys to LVGL control characters according to your key definitions*/
        switch(act_key) {
            case 1:
                act_key = LV_KEY_DOWN;
                break;
            case 2:
                act_key = LV_KEY_ENTER;
                break;
        }
        last_key = act_key;
    }
    else {
        data->state = LV_INDEV_STATE_REL;
    }

    data->key = last_key;
}

void lv_port_disp_init(void)
{
    /* 初始化LVGL显示设备 */

    /*-----------------------------
    * 创建一个绘图缓冲区
    *----------------------------*/
    /**
    * LVGL 需要一个缓冲区用来绘制小部件
    * 随后，这个缓冲区的内容会通过显示设备的 'flush_cb'(显示设备刷新函数) 
    * 复制到显示设备上
    * 这个缓冲区的大小需要大于显示设备一行的大小
    *
    * 这里有 3 种缓冲配置:
    * 1. 单缓冲区:
    * LVGL 会将显示设备的内容绘制到这里，并将他写入显示设备。
    *
    * 2. 双缓冲区:
    * LVGL 会将显示设备的内容绘制到其中一个缓冲区，并将他写入显示设备。
    * 需要使用 DMA 将要显示在显示设备的内容写入缓冲区。
    * 当数据从第一个缓冲区发送时，它将使 LVGL 能够将屏幕的下一部分绘制到另一个缓冲区。
    * 这样使得渲染和刷新可以并行执行。
    *
    * 3. 全尺寸双缓冲区
    * 设置两个屏幕大小的全尺寸缓冲区，并且设置 disp_drv.full_refresh = 1。
    * 这样，LVGL 将始终以 'flush_cb' 的形式提供整个渲染屏幕，只需更改帧缓冲区地址。
    */

    /* 使用双缓冲 */
    lv_color_t *buf1 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    // assert(buf1 != NULL);
    lv_color_t *buf2 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);

    /* 初始化显示缓冲区,保存显示缓冲区信息的结构体 */
    static lv_disp_draw_buf_t disp_buf;

    /* 初始化显示缓冲区 */
    uint32_t size_in_px = DISP_BUF_SIZE;
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, size_in_px);

    /* 在 LVGL 中注册显示设备 */
    static lv_disp_drv_t disp_drv;
    /* 显示设备的描述符(要注册的显示驱动程序、与显示交互并处理与图形相关的结构体、回调函数)*/
    /* 初始化显示设备 */
    lv_disp_drv_init(&disp_drv);
    /* 设置显示设备的分辨率
    ** 根据屏幕的大小设置
    */
    disp_drv.hor_res = LV_HOR_RES_MAX;
    disp_drv.ver_res = LV_VER_RES_MAX;
    /* 用来将缓冲区的内容复制到显示设备 */
    disp_drv.flush_cb = disp_driver_flush;
    /* 设置显示缓冲区 */
    disp_drv.draw_buf = &disp_buf;
    /* 注册显示设备 */
    lv_disp_drv_register(&disp_drv);
}

void lv_port_indev_button(void)
{
    // 初始化输入设备--按键
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);             /* 初始化输入设备驱动 */
    indev_drv.read_cb = boot_btn_button_cb;           /* 设置输入设备的读取函数 */
    indev_drv.type = LV_INDEV_TYPE_BUTTON;     /* 设置输入设备的类型*/
    /* 在LVGL中注册驱动程序，并保存创建的输入设备对象 */
    lv_indev_t * indev_bootbtn;  
    indev_bootbtn = lv_indev_drv_register(&indev_drv);

    // printf("free_heap_size = %ld\n", esp_get_free_heap_size());
    // printf("in app_main the min free stack size is %ld \r\n", (int32_t)uxTaskGetStackHighWaterMark(NULL));

    // /*Assign buttons to points on the screen*/
    static const lv_point_t btn_points[1] = {{35, 40}};

    lv_indev_set_button_points(indev_bootbtn, btn_points);
}

void lv_port_indev_keypad(void)
{
    // 初始化输入设备--键盘
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);             /* 初始化输入设备驱动 */
    indev_drv.read_cb = keypad_read;           /* 设置输入设备的读取函数 */
    indev_drv.type = LV_INDEV_TYPE_KEYPAD;     /* 设置输入设备的类型*/
    /* 在LVGL中注册驱动程序，并保存创建的输入设备对象 */
    lv_indev_t * indev_bootbtn;  
    indev_bootbtn = lv_indev_drv_register(&indev_drv);

    // printf("free_heap_size = %ld\n", esp_get_free_heap_size());
    // printf("in app_main the min free stack size is %ld \r\n", (int32_t)uxTaskGetStackHighWaterMark(NULL));
    
    // 创建一个LVGL对象组
    btn_group = lv_group_create();
    
    // 将按钮添加到组中
    // lv_group_add_obj(group, btn);

    // 将输入设备绑定到组
    lv_indev_set_group(indev_bootbtn, btn_group);
}

void lv_port_indev_init(void)
{
    /**
     * LittelvGL 支持的输入设备的示例实现
     * 触摸板
     * 鼠标（支持光标）
     * 按键盘（仅支持 GUI 使用）
     * 编码器（仅支持 GUI 使用：左、右、按下）
     * 按钮（外部按钮用于按压屏幕上的点）
     */

    // 初始化button,需要绑定按键和点击的坐标
    // lv_port_indev_button();

    // keypad对应键盘,只能映射成以下的按键值
    // LV_KEY_UP        = 17,  /*0x11*/
    // LV_KEY_DOWN      = 18,  /*0x12*/
    // LV_KEY_RIGHT     = 19,  /*0x13*/
    // LV_KEY_LEFT      = 20,  /*0x14*/
    // LV_KEY_ESC       = 27,  /*0x1B*/
    // LV_KEY_DEL       = 127, /*0x7F*/
    // LV_KEY_BACKSPACE = 8,   /*0x08*/
    // LV_KEY_ENTER     = 10,  /*0x0A, '\n'*/
    // LV_KEY_HOME      = 2,   /*0x02, STX*/
    // LV_KEY_END       = 3,   /*0x03, ETX*/
    lv_port_indev_keypad();
}

void lvgl_gui_init()
{
    // LCD背光管脚无作用,3.3V屏幕就亮

    // LVGL显示设备和输入设备初始化有顺序要求

    /* Initialize SPI or I2C bus used by the drivers */
    /* 通过lvgl_esp32_drivers库初始化显示设备 RGBLCD */
    /* 修改st7735s_set_orientation函数,更改输出颜色为RGB */
    /** 对于非128*160的屏幕修改st7735s.h中的COLSTART和ROWSTART是否跳过部分像素
     *   128*128屏幕 COLSTART = 0, ROWSTART = 32
     */
    lvgl_driver_init();

    /* 初始化 LVGL 图形库 */
    lv_init();

    /* 初始化显示设备 */
    lv_port_disp_init();

    /* 初始化输入设备 */
    lv_port_indev_init();
}
